﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.IO.Ports;
using Modbus.Device;
using Modbus.Utility;
using System.Diagnostics;

namespace modBusTest
{
    class command
    {
        public enum value_t { float_t, word_t }
        public enum RW { read, write }
        public enum regType { holding_t, input_t }

        private RW RWvalue;
        private value_t WordFloatValue;
        private regType registerType;

        private int address;
        private object data;
        private string origCommand;
        private string returnCode;


        static public string comPort;
        static public int modbusId;

        public command(string stringToParse)
        {
            parseLine(stringToParse);
        }


        public long writeValue()
        {
            using (SerialPort port = new SerialPort(comPort))
            {
                // configure serial port
                port.BaudRate = 115200;
                port.DataBits = 8;
                port.Parity = Parity.None;
                port.StopBits = StopBits.One;

                try
                {
                    port.Open();
                }
                catch (Exception err)
                {
                    throw err;
                }

                // create modbus master
                IModbusSerialMaster master = ModbusSerialMaster.CreateRtu(port);

                byte slaveID = (byte)modbusId;
                master.Transport.ReadTimeout = 50;

                // actually execute command

                long timeTaken = 0;
                returnCode = null;
                try
                {
                    timeTaken = preformCommunication(master, slaveID);
                }
                catch (Exception err)
                {
                    returnCode = err.Message;
                }

                if (returnCode == null)
                {
                    returnCode = "Ok";
                }

                return timeTaken;
            }
        }

        private long preformCommunication(IModbusSerialMaster master, byte slaveID)
        {
            Stopwatch timer = new Stopwatch();
            timer.Reset();
            timer.Start();
            //if input registers
            if (registerType == regType.input_t)
            {
                handleInputRegisterRead(master, slaveID);
                timer.Stop();
                return timer.ElapsedMilliseconds;
            }

            //if holding registers
            if (RWvalue == RW.read)
            {
                handleHoldingRegisterRead(master, slaveID);
            }

            if (RWvalue == RW.write)
            {
                HandleHoldingRegisterWrite(master, slaveID);
            }


            timer.Stop();
            return timer.ElapsedMilliseconds;
        }

        private void HandleHoldingRegisterWrite(IModbusSerialMaster master, byte slaveID)
        {
            if (WordFloatValue == value_t.word_t)
            {
                master.WriteSingleRegister(slaveID, (ushort)(address - 40000 - 1), ushort.Parse(((Int16)data).ToString()));
            }
            else
            {
                byte[] all = BitConverter.GetBytes((float)data);
                ushort[] array = new ushort[2];
                array[0] = BitConverter.ToUInt16(all, 0);
                array[1] = BitConverter.ToUInt16(all, 2);

                master.WriteMultipleRegisters(slaveID, (ushort)(address - 40000 - 1), array);
            }
        }

        private void handleHoldingRegisterRead(IModbusSerialMaster master, byte slaveID)
        {
            if (WordFloatValue == value_t.word_t)
            {
                ushort[] input = master.ReadHoldingRegisters(slaveID, (ushort)(address - 40000 - 1), 1);
                data = input[0];
            }
            else
            {
                ushort[] input = master.ReadHoldingRegisters(slaveID, (ushort)(address - 40000 - 1), 2);
                data = ModbusUtility.GetSingle(input[0], input[1]);
            }
        }

        private void handleInputRegisterRead(IModbusSerialMaster master, byte slaveID)
        {
            if (WordFloatValue == value_t.word_t)
            {
                ushort[] input = master.ReadHoldingRegisters(slaveID, (ushort)(address - 30000 - 1), 1);
                data = input[0];
            }
            else
            {
                ushort[] input = master.ReadHoldingRegisters(slaveID, (ushort)(address - 30000 - 1), 2);
                data = ModbusUtility.GetSingle(input[0], input[1]);
            }
        }


        private command parseLine(string lineToRead)
        {

            origCommand = lineToRead;
            string[] splitLine = lineToRead.Split(' ', ',');

            if (!(splitLine.Length == 3 || splitLine.Length == 4))
            {
                throw new Exception("Input has incorrect number of arguments");
            }

            //see if it is a read or write command
            parseRW(splitLine);

            //is the data type word or float
            parseWF(splitLine);

            //get the address
            address = Int32.Parse(splitLine[2]);

            parseRegisterType();

            //get the data stored at that value
            parseData(splitLine);
            return this;
        }

        private void parseRegisterType()
        {
            if (address <= 400000)
            {
                registerType = regType.input_t;
            }
            else
            {
                registerType = regType.holding_t;
            }
        }

        private void parseData(string[] splitLine)
        {
            if (RWvalue == command.RW.write)
            {
                if (WordFloatValue == command.value_t.word_t)
                {
                    data = new Int16();
                    data = Int16.Parse(splitLine[3]);
                }
                else
                {
                    data = new float();
                    data = float.Parse(splitLine[3]);
                }
            }
        }

        private void parseWF(string[] splitLine)
        {
            if (splitLine[1] == "f")
            {
                WordFloatValue = command.value_t.float_t;
            }
            else if (splitLine[1] == "w")
            {
                WordFloatValue = command.value_t.word_t;
            }
            else
            {
                throw new Exception("Invalid float word symbol");
            }
        }

        private void parseRW(string[] splitLine)
        {
            if (splitLine[0] == "w")
            {
                RWvalue = command.RW.write;
            }
            else if (splitLine[0] == "r")
            {
                RWvalue = command.RW.read;
            }
            else
            {
                throw new Exception("Invalid read write symbol");
            }
        }

        public string[] getResults()
        {
            string[] temp = new string[3];
            temp[0] = origCommand;
            temp[1] = returnCode;
            temp[2] = data.ToString();
            return temp;
        }

    }
}
